Continuing my explortion of Notification of Infectious Disease data, I’ve been working on an interactive map of where disease have been reported in England and Wales. I found this link a useful resource https://journocode.com/2016/01/28/your-first-choropleth-map/.

Aquiring and processing data

I was able to download data from the Notification of Infectious Disease website on the distrubtion of cases by local authority (“Statutory notifiable diseases - number of cases reported in week 51 of 2017”). I’ve limited this experimental analysis to one week of data. I did some refomatting in Excel and saved this as a .csv for speed and convinience, before loading it into R. I then explicitly set NA data to 0 (assuming no notifications means no cases). I also removed the string " UA" from where it appeared in local authority names.

library("reshape2")
library("magrittr")
geo_disease_data<-read.csv("geo_disease_data.csv")
geo_disease_data[is.na(geo_disease_data)] <- 0
geo_disease_data$LocalAuthority<-gsub(" UA", "", geo_disease_data$LocalAuthority)

The next task was to get some map data for local authorities. I played around with a few different maps from the Office for National Statistics Open Geography Portal (I’m new to geo-spatial data). This one seemed to be the best:

library("rgdal")
LocalAuthorities<-readOGR("https://opendata.arcgis.com/datasets/686603e943f948acaa13fb5d2b0f1275_4.geojson")
OGR data source with driver: GeoJSON 
Source: "https://opendata.arcgis.com/datasets/686603e943f948acaa13fb5d2b0f1275_4.geojson", layer: "686603e943f948acaa13fb5d2b0f1275_4"
with 380 features
It has 10 fields

I then merged the two data sets by local authority name.

LA_Diseases<-sp::merge(LocalAuthorities, geo_disease_data, by.x="lad16nm", by.y="LocalAuthority")

The map

The next task was to display the data as a map. For this I used leaflet. This is a wrapper for a Javascrip “App”. I’ve put some comments in the code (after the map) to explain what’s going on at each step.

library("leaflet")

diseases <- names(geo_disease_data)[-1] #Get a list of disease names (the first colmn name here is local authority name)

max_cases <- max(melt(geo_disease_data, id.vars="LocalAuthority")$value) #Get the maximum number of cases for any disease

#Set up a colour scale from 0 to the maximum number of diseases
pal <- colorNumeric(
   palette = "viridis",
   domain = c(0,max_cases)
 )

#Initialise the map with the legend and a control to allow users to select a disease
myleaflet<-leaflet(width = "100%") %>% 
  addLegend( pal = pal, 
             values = 0:max_cases,
             title = "Cases",
             opacity = 1) %>%
  addLayersControl(baseGroups=diseases, 
                   position = "bottomleft", 
                   options = layersControlOptions(collapsed = TRUE))

#Add a layer to the map for each disease
for (active_disease in diseases) 
  {
  myleaflet <- myleaflet %>%
    addPolygons(data=LA_Diseases, 
              fillColor=~pal(LA_Diseases[[active_disease]]),
              fillOpacity = 0.8,
              color = "black",
              weight = 1,
              popup = paste(LA_Diseases$lad16nm, LA_Diseases[[active_disease]],"cases"), #This popup shows the local authority name and number of cases
              group = active_disease
              )
  }    

myleaflet<-myleaflet %>% showGroup("Scarlet.fever") #Pick an interesting disease as default

myleaflet #show the map

Evaluation and conclusions

I have a few problems! Performance on my mobile is not great, but aside from this the main issue is that I don’t have data for all of the local authorities. I thought I would try to find out why so I got a list of local authorities from the disease data and another list from the map.

Unique_LA_Map<-data.frame(LocalAuthority=unique(LocalAuthorities$lad16nm) %>% sort())
Unique_LA_Diseases<-data.frame(LocalAuthority=unique(geo_disease_data$LocalAuthority) %>% sort())

Next, I did some joins to see how many of the names were shared between the data sets and how many were unique to each.

In_Map_Not_Diseases<-data.frame(LocalAuthority=
  Unique_LA_Map$LocalAuthority[!(Unique_LA_Map$LocalAuthority %in% Unique_LA_Diseases$LocalAuthority)])
In_Diseases_Not_Map<-data.frame(LocalAuthority=
  Unique_LA_Diseases$LocalAuthority[!(Unique_LA_Diseases$LocalAuthority %in% Unique_LA_Map$LocalAuthority)])
In_Both<-base::merge(Unique_LA_Map,Unique_LA_Diseases, by="LocalAuthority")

I’ve plotted the results on a Venn diagram. Not the most beautiful, but quite informative.

library(VennDiagram)
grid.newpage()
draw.pairwise.venn(area1 = nrow(Unique_LA_Diseases), 
                   area2 = nrow(Unique_LA_Map), 
                   cross.area = nrow(In_Both), 
                   category = c("n in Diseases Data", "n in Map Data"),
                   fill = c("blue", "red")
                   )
(polygon[GRID.polygon.21], polygon[GRID.polygon.22], polygon[GRID.polygon.23], polygon[GRID.polygon.24], text[GRID.text.25], text[GRID.text.26], lines[GRID.lines.27], text[GRID.text.28], text[GRID.text.29], text[GRID.text.30]) 

276 local authorities are in both data sets, 104 are only in the map data and 12 are unique to the disease data. A large part of the explaination of this is that the map includes scottish local authorities, but the disease data is only for England and Wales. Looking at the data frames, there are some instances where the name is stated differently in each set - despite referring to the same authority e.g. “City of Kingston upon Hull” and “Kingston upon Hull, City of”. And there are some like “East Dorset” that are on the map but don’t seem to have a match at all.

My conclusion is that, whilst making maps of infectious disease occurances is a great way of getting insights out of the data, I would need to access some domain knowledge on mapping local authorities to take this much further.

See my website for more examples. Contact me on bob dot turner dot uk at gmail dot com with comments or questions.

Contains public sector information licensed under the Open Government Licence v3.0.

LS0tDQp0aXRsZTogIk1hcCBvZiBJbmZlY3Rpb3VzIERpc2Vhc2UgTm90aWZpY2F0aW9ucyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQotLS0NCg0KQ29udGludWluZyBteSBleHBsb3J0aW9uIG9mIFtOb3RpZmljYXRpb24gb2YgSW5mZWN0aW91cyBEaXNlYXNlXShodHRwczovL3d3dy5nb3YudWsvZ292ZXJubWVudC9jb2xsZWN0aW9ucy9ub3RpZmljYXRpb25zLW9mLWluZmVjdGlvdXMtZGlzZWFzZXMtbm9pZHMpIGRhdGEsIEkndmUgYmVlbiB3b3JraW5nIG9uIGFuIGludGVyYWN0aXZlIG1hcCBvZiB3aGVyZSBkaXNlYXNlIGhhdmUgYmVlbiByZXBvcnRlZCBpbiBFbmdsYW5kIGFuZCBXYWxlcy4gSSBmb3VuZCB0aGlzIGxpbmsgYSB1c2VmdWwgcmVzb3VyY2UgaHR0cHM6Ly9qb3Vybm9jb2RlLmNvbS8yMDE2LzAxLzI4L3lvdXItZmlyc3QtY2hvcm9wbGV0aC1tYXAvLg0KDQojI0FxdWlyaW5nIGFuZCBwcm9jZXNzaW5nIGRhdGENCg0KSSB3YXMgYWJsZSB0byBkb3dubG9hZCBkYXRhIGZyb20gdGhlIFtOb3RpZmljYXRpb24gb2YgSW5mZWN0aW91cyBEaXNlYXNlXShodHRwczovL3d3dy5nb3YudWsvZ292ZXJubWVudC9jb2xsZWN0aW9ucy9ub3RpZmljYXRpb25zLW9mLWluZmVjdGlvdXMtZGlzZWFzZXMtbm9pZHMpIHdlYnNpdGUgb24gdGhlIGRpc3RydWJ0aW9uIG9mIGNhc2VzIGJ5IGxvY2FsIGF1dGhvcml0eSAoIlN0YXR1dG9yeSBub3RpZmlhYmxlIGRpc2Vhc2VzICAtIG51bWJlciBvZiBjYXNlcyByZXBvcnRlZCBpbiB3ZWVrIDUxIG9mIDIwMTciKS4gSSd2ZSBsaW1pdGVkIHRoaXMgZXhwZXJpbWVudGFsIGFuYWx5c2lzIHRvIG9uZSB3ZWVrIG9mIGRhdGEuIEkgZGlkIHNvbWUgcmVmb21hdHRpbmcgaW4gRXhjZWwgYW5kIHNhdmVkIHRoaXMgYXMgYSAuY3N2IGZvciBzcGVlZCBhbmQgY29udmluaWVuY2UsIGJlZm9yZSBsb2FkaW5nIGl0IGludG8gUi4gSSB0aGVuIGV4cGxpY2l0bHkgc2V0IE5BIGRhdGEgdG8gMCAoYXNzdW1pbmcgbm8gbm90aWZpY2F0aW9ucyBtZWFucyBubyBjYXNlcykuIEkgYWxzbyByZW1vdmVkIHRoZSBzdHJpbmcgIiBVQSIgZnJvbSB3aGVyZSBpdCBhcHBlYXJlZCBpbiBsb2NhbCBhdXRob3JpdHkgbmFtZXMuCQkJCQ0KDQpgYGB7cn0NCg0KbGlicmFyeSgicmVzaGFwZTIiKQ0KbGlicmFyeSgibWFncml0dHIiKQ0KDQpnZW9fZGlzZWFzZV9kYXRhPC1yZWFkLmNzdigiZ2VvX2Rpc2Vhc2VfZGF0YS5jc3YiKQ0KDQpnZW9fZGlzZWFzZV9kYXRhW2lzLm5hKGdlb19kaXNlYXNlX2RhdGEpXSA8LSAwDQoNCmdlb19kaXNlYXNlX2RhdGEkTG9jYWxBdXRob3JpdHk8LWdzdWIoIiBVQSIsICIiLCBnZW9fZGlzZWFzZV9kYXRhJExvY2FsQXV0aG9yaXR5KQ0KYGBgDQoNClRoZSBuZXh0IHRhc2sgd2FzIHRvIGdldCBzb21lIG1hcCBkYXRhIGZvciBsb2NhbCBhdXRob3JpdGllcy4gSSBwbGF5ZWQgYXJvdW5kIHdpdGggYSBmZXcgZGlmZmVyZW50IG1hcHMgZnJvbSBbdGhlIE9mZmljZSBmb3IgTmF0aW9uYWwgU3RhdGlzdGljcyBPcGVuIEdlb2dyYXBoeSBQb3J0YWxdKGh0dHA6Ly9nZW9wb3J0YWwuc3RhdGlzdGljcy5nb3YudWsvZGF0YXNldHMvKSAoSSdtIG5ldyB0byBnZW8tc3BhdGlhbCBkYXRhKS4gVGhpcyBvbmUgc2VlbWVkIHRvIGJlIHRoZSBiZXN0Og0KDQpgYGB7ciwgcmVzdWx0cz0iSGlkZSJ9DQoNCmxpYnJhcnkoInJnZGFsIikNCg0KTG9jYWxBdXRob3JpdGllczwtcmVhZE9HUigiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzY4NjYwM2U5NDNmOTQ4YWNhYTEzZmI1ZDJiMGYxMjc1XzQuZ2VvanNvbiIpDQoNCmBgYA0KDQpJIHRoZW4gbWVyZ2VkIHRoZSB0d28gZGF0YSBzZXRzIGJ5IGxvY2FsIGF1dGhvcml0eSBuYW1lLg0KDQpgYGB7cn0NCg0KTEFfRGlzZWFzZXM8LXNwOjptZXJnZShMb2NhbEF1dGhvcml0aWVzLCBnZW9fZGlzZWFzZV9kYXRhLCBieS54PSJsYWQxNm5tIiwgYnkueT0iTG9jYWxBdXRob3JpdHkiKQ0KDQpgYGANCg0KDQojI1RoZSBtYXANCg0KVGhlIG5leHQgdGFzayB3YXMgdG8gZGlzcGxheSB0aGUgZGF0YSBhcyBhIG1hcC4gRm9yIHRoaXMgSSB1c2VkIFtsZWFmbGV0XShodHRwOi8vbGVhZmxldGpzLmNvbS8pLiBUaGlzIGlzIGEgd3JhcHBlciBmb3IgYSBKYXZhc2NyaXAgIkFwcCIuIEkndmUgcHV0IHNvbWUgY29tbWVudHMgaW4gdGhlIGNvZGUgKGFmdGVyIHRoZSBtYXApIHRvIGV4cGxhaW4gd2hhdCdzIGdvaW5nIG9uIGF0IGVhY2ggc3RlcC4NCg0KYGBge3IsIGVjaG89RkFMU0V9DQoNCmxpYnJhcnkoImxlYWZsZXQiKQ0KDQpkaXNlYXNlcyA8LSBuYW1lcyhnZW9fZGlzZWFzZV9kYXRhKVstMV0gI0dldCBhIGxpc3Qgb2YgZGlzZWFzZSBuYW1lcyAodGhlIGZpcnN0IGNvbG1uIG5hbWUgaGVyZSBpcyBsb2NhbCBhdXRob3JpdHkgbmFtZSkNCg0KbWF4X2Nhc2VzIDwtIG1heChtZWx0KGdlb19kaXNlYXNlX2RhdGEsIGlkLnZhcnM9IkxvY2FsQXV0aG9yaXR5IikkdmFsdWUpICNHZXQgdGhlIG1heGltdW0gbnVtYmVyIG9mIGNhc2VzIGZvciBhbnkgZGlzZWFzZQ0KDQojU2V0IHVwIGEgY29sb3VyIHNjYWxlIGZyb20gMCB0byB0aGUgbWF4aW11bSBudW1iZXIgb2YgZGlzZWFzZXMNCnBhbCA8LSBjb2xvck51bWVyaWMoDQogICBwYWxldHRlID0gInZpcmlkaXMiLA0KICAgZG9tYWluID0gYygwLG1heF9jYXNlcykNCiApDQoNCiNJbml0aWFsaXNlIHRoZSBtYXAgd2l0aCB0aGUgbGVnZW5kIGFuZCBhIGNvbnRyb2wgdG8gYWxsb3cgdXNlcnMgdG8gc2VsZWN0IGEgZGlzZWFzZQ0KbXlsZWFmbGV0PC1sZWFmbGV0KHdpZHRoID0gIjEwMCUiKSAlPiUgDQogIGFkZExlZ2VuZCggcGFsID0gcGFsLCANCiAgICAgICAgICAgICB2YWx1ZXMgPSAwOm1heF9jYXNlcywNCiAgICAgICAgICAgICB0aXRsZSA9ICJDYXNlcyIsDQogICAgICAgICAgICAgb3BhY2l0eSA9IDEpICU+JQ0KICBhZGRMYXllcnNDb250cm9sKGJhc2VHcm91cHM9ZGlzZWFzZXMsIA0KICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiLCANCiAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gVFJVRSkpDQoNCiNBZGQgYSBsYXllciB0byB0aGUgbWFwIGZvciBlYWNoIGRpc2Vhc2UNCmZvciAoYWN0aXZlX2Rpc2Vhc2UgaW4gZGlzZWFzZXMpIA0KICB7DQogIG15bGVhZmxldCA8LSBteWxlYWZsZXQgJT4lDQogICAgYWRkUG9seWdvbnMoZGF0YT1MQV9EaXNlYXNlcywgDQogICAgICAgICAgICAgIGZpbGxDb2xvcj1+cGFsKExBX0Rpc2Vhc2VzW1thY3RpdmVfZGlzZWFzZV1dKSwNCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjgsDQogICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMSwNCiAgICAgICAgICAgICAgcG9wdXAgPSBwYXN0ZShMQV9EaXNlYXNlcyRsYWQxNm5tLCBMQV9EaXNlYXNlc1tbYWN0aXZlX2Rpc2Vhc2VdXSwiY2FzZXMiKSwgI1RoaXMgcG9wdXAgc2hvd3MgdGhlIGxvY2FsIGF1dGhvcml0eSBuYW1lIGFuZCBudW1iZXIgb2YgY2FzZXMNCiAgICAgICAgICAgICAgZ3JvdXAgPSBhY3RpdmVfZGlzZWFzZQ0KICAgICAgICAgICAgICApDQogIH0gICAgDQoNCm15bGVhZmxldDwtbXlsZWFmbGV0ICU+JSBzaG93R3JvdXAoIlNjYXJsZXQuZmV2ZXIiKSAjUGljayBhbiBpbnRlcmVzdGluZyBkaXNlYXNlIGFzIGRlZmF1bHQNCg0KbXlsZWFmbGV0ICNzaG93IHRoZSBtYXANCg0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UgfQ0KDQpsaWJyYXJ5KCJsZWFmbGV0IikNCg0KZGlzZWFzZXMgPC0gbmFtZXMoZ2VvX2Rpc2Vhc2VfZGF0YSlbLTFdICNHZXQgYSBsaXN0IG9mIGRpc2Vhc2UgbmFtZXMgKHRoZSBmaXJzdCBjb2xtbiBuYW1lIGhlcmUgaXMgbG9jYWwgYXV0aG9yaXR5IG5hbWUpDQoNCm1heF9jYXNlcyA8LSBtYXgobWVsdChnZW9fZGlzZWFzZV9kYXRhLCBpZC52YXJzPSJMb2NhbEF1dGhvcml0eSIpJHZhbHVlKSAjR2V0IHRoZSBtYXhpbXVtIG51bWJlciBvZiBjYXNlcyBmb3IgYW55IGRpc2Vhc2UNCg0KI1NldCB1cCBhIGNvbG91ciBzY2FsZSBmcm9tIDAgdG8gdGhlIG1heGltdW0gbnVtYmVyIG9mIGRpc2Vhc2VzDQpwYWwgPC0gY29sb3JOdW1lcmljKA0KICAgcGFsZXR0ZSA9ICJ2aXJpZGlzIiwNCiAgIGRvbWFpbiA9IGMoMCxtYXhfY2FzZXMpDQogKQ0KDQojSW5pdGlhbGlzZSB0aGUgbWFwIHdpdGggdGhlIGxlZ2VuZCBhbmQgYSBjb250cm9sIHRvIGFsbG93IHVzZXJzIHRvIHNlbGVjdCBhIGRpc2Vhc2UNCm15bGVhZmxldDwtbGVhZmxldCh3aWR0aCA9ICIxMDAlIikgJT4lIA0KICBhZGRMZWdlbmQoIHBhbCA9IHBhbCwgDQogICAgICAgICAgICAgdmFsdWVzID0gMDptYXhfY2FzZXMsDQogICAgICAgICAgICAgdGl0bGUgPSAiQ2FzZXMiLA0KICAgICAgICAgICAgIG9wYWNpdHkgPSAxKSAlPiUNCiAgYWRkTGF5ZXJzQ29udHJvbChiYXNlR3JvdXBzPWRpc2Vhc2VzLCANCiAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJib3R0b21sZWZ0IiwgDQogICAgICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKGNvbGxhcHNlZCA9IFRSVUUpKQ0KDQojQWRkIGEgbGF5ZXIgdG8gdGhlIG1hcCBmb3IgZWFjaCBkaXNlYXNlDQpmb3IgKGFjdGl2ZV9kaXNlYXNlIGluIGRpc2Vhc2VzKSANCiAgew0KICBteWxlYWZsZXQgPC0gbXlsZWFmbGV0ICU+JQ0KICAgIGFkZFBvbHlnb25zKGRhdGE9TEFfRGlzZWFzZXMsIA0KICAgICAgICAgICAgICBmaWxsQ29sb3I9fnBhbChMQV9EaXNlYXNlc1tbYWN0aXZlX2Rpc2Vhc2VdXSksDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC44LA0KICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgIHdlaWdodCA9IDEsDQogICAgICAgICAgICAgIHBvcHVwID0gcGFzdGUoTEFfRGlzZWFzZXMkbGFkMTZubSwgTEFfRGlzZWFzZXNbW2FjdGl2ZV9kaXNlYXNlXV0sImNhc2VzIiksICNUaGlzIHBvcHVwIHNob3dzIHRoZSBsb2NhbCBhdXRob3JpdHkgbmFtZSBhbmQgbnVtYmVyIG9mIGNhc2VzDQogICAgICAgICAgICAgIGdyb3VwID0gYWN0aXZlX2Rpc2Vhc2UNCiAgICAgICAgICAgICAgKQ0KICB9ICAgIA0KDQpteWxlYWZsZXQ8LW15bGVhZmxldCAlPiUgc2hvd0dyb3VwKCJTY2FybGV0LmZldmVyIikgI1BpY2sgYW4gaW50ZXJlc3RpbmcgZGlzZWFzZSBhcyBkZWZhdWx0DQoNCm15bGVhZmxldCAjc2hvdyB0aGUgbWFwDQoNCmBgYA0KDQojI0V2YWx1YXRpb24gYW5kIGNvbmNsdXNpb25zDQoNCkkgaGF2ZSBhIGZldyBwcm9ibGVtcyEgUGVyZm9ybWFuY2Ugb24gbXkgbW9iaWxlIGlzIG5vdCBncmVhdCwgYnV0IGFzaWRlIGZyb20gdGhpcyB0aGUgbWFpbiBpc3N1ZSBpcyB0aGF0IEkgZG9uJ3QgaGF2ZSBkYXRhIGZvciBhbGwgb2YgdGhlIGxvY2FsIGF1dGhvcml0aWVzLiBJIHRob3VnaHQgSSB3b3VsZCB0cnkgdG8gZmluZCBvdXQgd2h5IHNvIEkgZ290IGEgbGlzdCBvZiBsb2NhbCBhdXRob3JpdGllcyBmcm9tIHRoZSBkaXNlYXNlIGRhdGEgYW5kIGFub3RoZXIgbGlzdCBmcm9tIHRoZSBtYXAuDQoNCmBgYHtyfQ0KVW5pcXVlX0xBX01hcDwtZGF0YS5mcmFtZShMb2NhbEF1dGhvcml0eT11bmlxdWUoTG9jYWxBdXRob3JpdGllcyRsYWQxNm5tKSAlPiUgc29ydCgpKQ0KVW5pcXVlX0xBX0Rpc2Vhc2VzPC1kYXRhLmZyYW1lKExvY2FsQXV0aG9yaXR5PXVuaXF1ZShnZW9fZGlzZWFzZV9kYXRhJExvY2FsQXV0aG9yaXR5KSAlPiUgc29ydCgpKQ0KYGBgDQoNCk5leHQsIEkgZGlkIHNvbWUgam9pbnMgdG8gc2VlIGhvdyBtYW55IG9mIHRoZSBuYW1lcyB3ZXJlIHNoYXJlZCBiZXR3ZWVuIHRoZSBkYXRhIHNldHMgYW5kIGhvdyBtYW55IHdlcmUgdW5pcXVlIHRvIGVhY2guDQoNCmBgYHtyfQ0KDQpJbl9NYXBfTm90X0Rpc2Vhc2VzPC1kYXRhLmZyYW1lKExvY2FsQXV0aG9yaXR5PQ0KICBVbmlxdWVfTEFfTWFwJExvY2FsQXV0aG9yaXR5WyEoVW5pcXVlX0xBX01hcCRMb2NhbEF1dGhvcml0eSAlaW4lIFVuaXF1ZV9MQV9EaXNlYXNlcyRMb2NhbEF1dGhvcml0eSldKQ0KDQpJbl9EaXNlYXNlc19Ob3RfTWFwPC1kYXRhLmZyYW1lKExvY2FsQXV0aG9yaXR5PQ0KICBVbmlxdWVfTEFfRGlzZWFzZXMkTG9jYWxBdXRob3JpdHlbIShVbmlxdWVfTEFfRGlzZWFzZXMkTG9jYWxBdXRob3JpdHkgJWluJSBVbmlxdWVfTEFfTWFwJExvY2FsQXV0aG9yaXR5KV0pDQoNCkluX0JvdGg8LWJhc2U6Om1lcmdlKFVuaXF1ZV9MQV9NYXAsVW5pcXVlX0xBX0Rpc2Vhc2VzLCBieT0iTG9jYWxBdXRob3JpdHkiKQ0KYGBgDQoNCkkndmUgcGxvdHRlZCB0aGUgcmVzdWx0cyBvbiBhICoqVmVubiBkaWFncmFtKiouIE5vdCB0aGUgbW9zdCBiZWF1dGlmdWwsIGJ1dCBxdWl0ZSBpbmZvcm1hdGl2ZS4NCg0KYGBge3J9DQoNCmxpYnJhcnkoVmVubkRpYWdyYW0pDQpncmlkLm5ld3BhZ2UoKQ0KZHJhdy5wYWlyd2lzZS52ZW5uKGFyZWExID0gbnJvdyhVbmlxdWVfTEFfRGlzZWFzZXMpLCANCiAgICAgICAgICAgICAgICAgICBhcmVhMiA9IG5yb3coVW5pcXVlX0xBX01hcCksIA0KICAgICAgICAgICAgICAgICAgIGNyb3NzLmFyZWEgPSBucm93KEluX0JvdGgpLCANCiAgICAgICAgICAgICAgICAgICBjYXRlZ29yeSA9IGMoIm4gaW4gRGlzZWFzZXMgRGF0YSIsICJuIGluIE1hcCBEYXRhIiksDQogICAgICAgICAgICAgICAgICAgZmlsbCA9IGMoImJsdWUiLCAicmVkIikNCiAgICAgICAgICAgICAgICAgICApDQoNCmBgYA0KDQoyNzYgbG9jYWwgYXV0aG9yaXRpZXMgYXJlIGluIGJvdGggZGF0YSBzZXRzLCAxMDQgYXJlIG9ubHkgaW4gdGhlIG1hcCBkYXRhIGFuZCAxMiBhcmUgdW5pcXVlIHRvIHRoZSBkaXNlYXNlIGRhdGEuIEEgbGFyZ2UgcGFydCBvZiB0aGUgZXhwbGFpbmF0aW9uIG9mIHRoaXMgaXMgdGhhdCB0aGUgbWFwIGluY2x1ZGVzIHNjb3R0aXNoIGxvY2FsIGF1dGhvcml0aWVzLCBidXQgdGhlIGRpc2Vhc2UgZGF0YSBpcyBvbmx5IGZvciBFbmdsYW5kIGFuZCBXYWxlcy4gTG9va2luZyBhdCB0aGUgZGF0YSBmcmFtZXMsIHRoZXJlIGFyZSBzb21lIGluc3RhbmNlcyB3aGVyZSB0aGUgbmFtZSBpcyBzdGF0ZWQgZGlmZmVyZW50bHkgaW4gZWFjaCBzZXQgLSBkZXNwaXRlIHJlZmVycmluZyB0byB0aGUgc2FtZSBhdXRob3JpdHkgZS5nLiAiYHIgSW5fRGlzZWFzZXNfTm90X01hcCRMb2NhbEF1dGhvcml0eVtbM11dYCIgYW5kICJgciBJbl9NYXBfTm90X0Rpc2Vhc2VzJExvY2FsQXV0aG9yaXR5W1s0Nl1dYCIuIEFuZCB0aGVyZSBhcmUgc29tZSBsaWtlICJgciBJbl9NYXBfTm90X0Rpc2Vhc2VzJExvY2FsQXV0aG9yaXR5W1syNV1dYCIgdGhhdCBhcmUgb24gdGhlIG1hcCBidXQgZG9uJ3Qgc2VlbSB0byBoYXZlIGEgbWF0Y2ggYXQgYWxsLg0KDQpNeSBjb25jbHVzaW9uIGlzIHRoYXQsIHdoaWxzdCBtYWtpbmcgbWFwcyBvZiBpbmZlY3Rpb3VzIGRpc2Vhc2Ugb2NjdXJhbmNlcyBpcyBhIGdyZWF0IHdheSBvZiBnZXR0aW5nIGluc2lnaHRzIG91dCBvZiB0aGUgZGF0YSwgSSB3b3VsZCBuZWVkIHRvIGFjY2VzcyBzb21lIGRvbWFpbiBrbm93bGVkZ2Ugb24gbWFwcGluZyBsb2NhbCBhdXRob3JpdGllcyB0byB0YWtlIHRoaXMgbXVjaCBmdXJ0aGVyLg0KDQpTZWUgbXkgW3dlYnNpdGVdKGh0dHBzOi8vc2l0ZXMuZ29vZ2xlLmNvbS92aWV3L3JvYmVydC10dXJuZXIpIGZvciBtb3JlIGV4YW1wbGVzLiBDb250YWN0IG1lIG9uIGJvYiBkb3QgdHVybmVyIGRvdCB1ayBhdCBnbWFpbCBkb3QgY29tIHdpdGggY29tbWVudHMgb3IgcXVlc3Rpb25zLg0KDQpDb250YWlucyBwdWJsaWMgc2VjdG9yIGluZm9ybWF0aW9uIGxpY2Vuc2VkIHVuZGVyIHRoZSBPcGVuIEdvdmVybm1lbnQgTGljZW5jZSB2My4wLg==